iT邦幫忙

2024 iThome 鐵人賽

DAY 24
1

Rust 作為一個系統編程語言,因其高效能和內建的記憶體安全性,逐漸在各領域獲得了廣泛應用,尤其是在 Web 開發中。雖然 Rust 本身並不是專門為 Web 開發設計的語言,但隨著許多高效能 Web 框架的出現,Rust 成為了一個極具潛力的後端 Web 開發語言。本篇文章將帶你認識 Rust 中的 Web 框架:Actix


Actix

Actix 是一個非常高效且自由度高的 Rust Web 框架,其基於 Actor 模型構建,簡單來說 Actix 提供了非同步的支持,使得它在大量並行要求的情境下能夠有出色的表現,它也是 Rust 生態系統中性能最高的 Web 框架之一。

一、Actix 簡單範例

首先讓我們建立一個 web 專用的專案 rust-web

cargo new rust-web
cd rust-web

然後我們需要引入 Actix套件,修改 Cargo.toml

[dependencies]
actix-web = "4"

接下來, main.rs 建立下面使用 Actix 開發的簡單 Web 應用程式:

use actix_web::{web, App, HttpResponse, HttpServer, Responder};

// 定義一個簡單的處理器,處理 HTTP 請求並回傳 "Hello from Actix!" 作為回應
async fn greet() -> impl Responder {
    // 回傳一個 HTTP 200 OK 狀態的回應,內容為 "Hello from Actix!"
    HttpResponse::Ok().body("Hello from Actix!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // 創建一個 Actix 伺服器
    HttpServer::new(|| {
        // 使用 App::new() 創建一個新的應用程式,並設置路由
        // 當收到 GET 請求到 "/" 路徑時,執行 `greet` 處理器
        App::new()
            .route("/", web::get().to(greet))
    })
    // 將伺服器綁定到本地的 127.0.0.1 地址與 8080 埠號
    .bind("127.0.0.1:8080")?
    // 啟動伺服器並開始處理請求
    .run()
    .await
}

範例說明

  • 非同步函數 async fnasync 關鍵字表示這是一個非同步函數,它可以在不阻塞主執行緒的情況下執行。這樣能夠提高伺服器處理多個併發請求的能力。
  • greet 函數:這是一個簡單的處理器,用來處理 HTTP 請求並返回一個 HTTP 回應。這裡我們使用 HttpResponse::Ok() 表示回應狀態為 200(成功),並在回應的主體中返回字串 "Hello from Actix!"。
  • impl Responder:這是一個特徵,表示 greet 函數的返回值可以用來回應 HTTP 請求。
    在這個例子中,我們建立了一個簡單的伺服器,處理來自根路徑 / 的 GET 請求,並返回一個簡單的 HTTP 回應。
  • #[actix_web::main]:這是 Actix 的一個特別屬性,它將函數標記為 Actix Web 的非同步主函數,讓程式能夠進行非同步運行。
  • HttpServer::new:創建一個新的 HTTP 伺服器。這個伺服器會接受一個閉包,並通過 App::new() 來設定應用程式,允許我們設置路由、處理器和中間件。
  • App::new():每個 Actix 應用程式都是通過 App::new() 創建的。這裡我們使用 route() 方法來定義當有 GET 請求發送到 / 路徑時,會呼叫我們之前定義的 greet 處理器。
    bind("127.0.0.1:8080"):綁定伺服器的地址和端口。這裡是將伺服器綁定到本地端 127.0.0.1 和端口 8080。
  • run()awaitrun() 啟動伺服器,並開始接收請求。await 用來非同步地等待伺服器運行的完成。

二、啟動 web server

接下來我們執行主程式

cargo run

就可以從瀏覽器看到 http://localhost:8080 開啟了 actix 網頁,如下所示:

https://ithelp.ithome.com.tw/upload/images/20241007/20121176jSWTUHNOLB.png

3. 透過模板回傳 HTML

Actix 不僅可以用來回傳純文字的 HTTP 回應,還可以輕鬆地將 HTML 頁面作為回應內容。這在需要動態生成頁面或渲染視圖時非常有用。Actix 支援多種模板引擎,這裡我們使用 Tera 模板引擎來展示如何回傳 HTML。


1. 引入 Tera 套件

首先,我們需要在 Cargo.toml 文件中添加 Tera 和 Actix-web 的模板依賴:

[dependencies]
actix-web = "4"
tera = "1.17"

然後,執行以下指令來更新並安裝依賴:

cargo build

2. 建立 Tera 模板

接下來,我們需要在專案目錄中創建一個資料夾來存放模板文件。可以在專案的根目錄下建立一個 templates 資料夾,並在其中添加一個名為 index.html 的模板文件:

<!-- templates/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Actix + Tera Example</title>
</head>
<body>
    <h1>Hello from Actix using Tera!</h1>
    <p>Welcome, {{ name }}!</p>
</body>
</html>

這個模板文件簡單地顯示了一個歡迎訊息,並允許我們動態插入一個 name 變數。


三、修改 main.rs 來渲染模板

現在我們可以修改 main.rs 來渲染這個模板。首先需要引入 Tera 模板引擎相關的模組,然後設置路由來回傳 HTML:

use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use tera::{Tera, Context};

// 定義一個處理器來渲染模板
async fn greet_with_template(tmpl: web::Data<Tera>) -> impl Responder {
    // 創建一個模板上下文,用來傳遞變數到模板中
    let mut ctx = Context::new();
    ctx.insert("name", "Actix User"); // 插入名為 "name" 的變數,值為 "Actix User"

    // 渲染模板並回傳 HTML
    // 使用 tmpl.render() 方法渲染模板 "index.html",若渲染失敗則回傳錯誤訊息
    let rendered = tmpl.render("index.html", &ctx).unwrap_or_else(|_| {
        "Error rendering template".to_string()
    });

    // 使用 HttpResponse 回傳渲染結果,並設定回應的內容類型為 HTML
    HttpResponse::Ok().content_type("text/html").body(rendered)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // 初始化 Tera 模板引擎,並指定模板的路徑為 "templates/**/*"
    let tera = Tera::new("templates/**/*").unwrap(); // 加載 templates 資料夾中的所有模板文件

    // 創建並啟動 Actix Web 伺服器
    HttpServer::new(move || {
        App::new()
            // 傳遞 Tera 模板引擎的 Data 給 Actix 應用
            .app_data(web::Data::new(tera.clone())) 
            // 定義路由,當訪問 "/" 路徑時,呼叫 greet_with_template 來渲染模板
            .route("/", web::get().to(greet_with_template)) 
    })
    // 將伺服器綁定到本地 IP 127.0.0.1,埠號為 8080
    .bind("127.0.0.1:8080")?
    // 啟動伺服器,並非同步地等待伺服器運行
    .run()
    .await
}

說明:

  • Tera::new("templates//*")**:我們使用 Tera::new() 來加載 templates 資料夾中的所有模板文件。
  • Context:這是一個類似 HashMap 的結構,可以用來將數據傳遞到模板中。這裡我們插入了一個名為 name 的變數,值為 "Actix User",然後會在 index.html 中進行顯示。
  • renderrender 方法用來渲染模板,將上下文中的變數插入到對應的模板位置,並返回一個 HTML 字串。

四、運行應用程式並測試

一切準備就緒後,我們可以執行專案:

cargo run

在瀏覽器中訪問 http://localhost:8080,你將看到以下畫面,HTML 模板已成功渲染:

Hello from Actix using Tera!
Welcome, Actix User!

實際瀏覽器畫面如下:
https://ithelp.ithome.com.tw/upload/images/20241007/20121176NTyl6fzoyx.png


五、動態傳遞參數

如果想要讓用戶透過 URL 動態傳遞參數,我們可以修改處理器來接受 name 參數:

use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use tera::{Tera, Context};

// 定義一個處理器來渲染模板,接受動態的 `name` 參數
// `name` 是從 URL 中動態傳入的路徑參數
async fn greet_with_template(tmpl: web::Data<Tera>, name: web::Path<String>) -> impl Responder {
    // 創建模板上下文,將變數傳遞給模板使用
    let mut ctx = Context::new();
    // 從 `name` 參數中取出值,並插入到模板上下文中
    ctx.insert("name", &name.into_inner());

    // 使用模板引擎渲染指定的 `index.html` 模板,並將上下文插入到模板中
    // 如果渲染失敗,則回傳 "Error rendering template"
    let rendered = tmpl.render("index.html", &ctx).unwrap_or_else(|_| {
        "Error rendering template".to_string()
    });

    // 回傳渲染結果,並設定回應的內容類型為 HTML
    HttpResponse::Ok().content_type("text/html").body(rendered)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // 初始化 Tera 模板引擎,指定模板文件路徑為 "templates/**/*"
    let tera = Tera::new("templates/**/*").unwrap();

    // 創建並啟動 HTTP 伺服器
    HttpServer::new(move || {
        // 新建一個 Actix 應用程式,並將 Tera 模板引擎傳遞進去
        App::new()
            // 將 Tera 引擎資料傳遞給應用程式以供後續使用
            .app_data(web::Data::new(tera.clone()))
            // 設定動態路由,當訪問 `/{name}` 路徑時,呼叫 `greet_with_template` 處理器
            .route("/{name}", web::get().to(greet_with_template)) 
    })
    // 綁定伺服器到本地 IP 127.0.0.1 和埠號 8080
    .bind("127.0.0.1:8080")?
    // 啟動伺服器並非同步等待運行
    .run()
    .await
}

現在訪問 http://localhost:8080/John,你會看到:

Hello from Actix using Tera!
Welcome, John!

實際畫面如圖:
https://ithelp.ithome.com.tw/upload/images/20241007/20121176RSSU84G5Ya.png

這樣我們就成功地透過 URL 動態與GET request傳遞參數給模板,帶入到 HTML 頁面內使用。


總結

在這篇文章中,我們介紹了 Actix,Actix 在大量並行的應用場景下特別有效,例如即時通訊應用、大型多人線上遊戲伺服器或需要處理大量 API 請求的微服務架構。這是因為 Actix 能夠利用 Actor 模型和非同步特性,輕鬆處理大量的並行請求而不會阻塞主執行緒。

我們首先從一個簡單的 Web 應用範例開始,展示如何使用 Actix 創建一個基本的伺服器,處理 HTTP 請求,並回傳靜態文本。接著,我們進一步討論了如何整合 Tera 模板引擎,來動態生成 HTML 頁面。透過模板,我們能夠輕鬆地將資料嵌入到網頁中,實現動態內容的呈現。

最後,我們演示了如何讓 Actix 的路由處理動態參數,透過 URL 傳遞變數到模板,生成不同的網頁內容。

使用步驟:

  1. 建立專案與引入依賴:我們首先使用 Cargo 建立了 Rust Web 專案,並引入 Actix 和 Tera 模板引擎的依賴。
  2. 處理 HTTP 請求:通過 HttpServerApp 設定路由,處理來自特定路徑的 GET 請求,並返回靜態或動態生成的內容。
  3. 模板整合:使用 Tera 模板引擎來管理 HTML 模板,並將變數插入到模板中動態渲染頁面。
  4. 動態路由與參數傳遞:設置動態路由,從 URL 中提取變數,並將其傳遞給模板進行處理。

注意事項:

  • 非同步特性:Actix 支援非同步操作,這讓它能在高效能、大量並行的應用場景中更具優勢。因此,在編寫處理器函數時,請確保使用 async 來啟用非同步特性,以提升伺服器的效能。
  • 錯誤處理:在渲染模板時,應處理好潛在的錯誤,並確保伺服器能夠回傳適當的錯誤訊息給用戶。
  • 模板路徑:請確認模板文件的路徑與名稱是否正確,Tera 預設會從指定的路徑中加載模板,若路徑配置錯誤會導致渲染失敗。

這篇文章讓我們對於 Actix 先有一個基礎概念,這將有助於我們介紹後續的 Restful API 與其他進階應用。


上一篇
[Day 23] Rust 的測試框架:單元測試 & 集成測試
下一篇
[Day 25] Rust 的 Web 應用(二):探索 Rocket 框架
系列文
從 Python 開發者的角度學習 Rust —— 從語法基礎到實戰應用30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言